package com.mworld.galaxy.bull.web.member;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springside.modules.web.Servlets;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.mworld.galaxy.base.service.account.ShiroDbRealm.ShiroUser;
import com.mworld.galaxy.bull.entity.MemberUser;
import com.mworld.galaxy.bull.entity.product.Category;
import com.mworld.galaxy.bull.entity.product.Link;
import com.mworld.galaxy.bull.entity.product.Product;
import com.mworld.galaxy.bull.entity.product.Shop;
import com.mworld.galaxy.bull.service.product.CategoryService;
import com.mworld.galaxy.bull.service.product.LinkService;
import com.mworld.galaxy.bull.service.product.ProductService;
import com.mworld.galaxy.bull.service.product.ShopService;

@Controller
@RequestMapping(value = "/member/shop")
public class ShopManagementController {

    private static final int ERR_CODE_MISSING_REQUIRED_DATA = 100001;
    private static final int ERR_CODE_OBJECT_NOT_FOUND = 100002;
    // private static final int ERR_CODE_VALIDATION_ERROR = 100003;
    // private static final int ERR_CODE_DATABASE_ERROR = 100004;

    private static Map<String, String> sortTypes = Maps.newLinkedHashMap();
    static {
        sortTypes.put("auto", "排序数");
    }

    @Autowired
    ShopService shopService;
    @Autowired
    CategoryService categoryService;
    @Autowired
    ProductService productService;
    @Autowired
    LinkService linkService;

    // @Autowired
    // LinkService linkService;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public String view(Model model) {
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        Shop shop;
        if (shops.getTotalElements() == 0) {
            shop = new Shop();
            MemberUser currentUser = new MemberUser();
            currentUser.setId(memberId);
            shop.setMemberUser(currentUser);
        } else {
            shop = shops.getContent().get(0);
        }
        model.addAttribute("shop", shop);
        return "member/shop-home";
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String update(Model model, @Valid @ModelAttribute("preloadShop") Shop shop) {
        shopService.saveShop(shop);
        model.addAttribute("shop", shop);
        return "redirect:/member/shop";
    }

    @RequestMapping(value = "category", method = RequestMethod.GET)
    public String viewCategory(Model model) {
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        if (shops.getTotalElements() == 0) {
            return handleShopDoesNotExistSituation(model, memberId);
        }
        model.addAttribute("shop", shops.getContent().get(0));
        return "member/category-list";
    }

    @RequestMapping(value = "product", method = RequestMethod.GET)
    public String searchProduct(@RequestParam(value = "sortType", defaultValue = "auto") String sortType,
            @RequestParam(value = "page", defaultValue = "1") int pageNumber, Model model, ServletRequest request) {
        Map<String, Object> searchParams = Servlets.getParametersStartingWith(request, "search_");
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        if (shops.getTotalElements() == 0) {
            return handleShopDoesNotExistSituation(model, memberId);
        }
        Shop shop = shops.getContent().get(0);
        Page<Category> category = categoryService.getShopCategory(shop.getId());
        List<Category> c0 = category.getContent();
        List<Category> c = new ArrayList<Category>(c0.size());
        for (int i = 0; i < c0.size(); i++) {
            c.add(c0.get(i));
        }
        Collections.sort(c, new CategorySorter(c));
        Page<Product> product = productService.searchProduct(shop.getId(), searchParams, pageNumber, 50, sortType);

        model.addAttribute("shop", shop);
        model.addAttribute("category", c);
        model.addAttribute("products", product);
        model.addAttribute("sortType", "auto");
        model.addAttribute("sortTypes", sortTypes);
        return "member/product-list";
    }

    @RequestMapping(value = "product/create", method = RequestMethod.GET)
    public String createProductForm(@RequestParam(value = "cid", required = false) Long cid, Model model) {
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        if (shops.getTotalElements() == 0) {
            return handleShopDoesNotExistSituation(model, memberId);
        }
        Shop shop = shops.getContent().get(0);
        // Page<Category> category =
        // categoryService.getShopCategory(shop.getId());
        // List<Category> c0 = category.getContent();
        // List<Category> c = new ArrayList<Category>(c0.size());
        // for (int i = 0; i < c0.size(); i++) {
        // c.add(c0.get(i));
        // }
        // Collections.sort(c, new CategorySorter(c));

        model.addAttribute("shop", shop);
        // model.addAttribute("category", c);
        if (cid != null && cid > 0) {
            model.addAttribute("cid", cid);
        }
        model.addAttribute("product", new Product());
        model.addAttribute("action", "create");
        return "member/product-form";
    }

    @RequestMapping(value = "product/create", method = RequestMethod.POST)
    public String createProduct(@Valid Product newProduct, @RequestParam(value = "cid") Long[] cids,
            RedirectAttributes redirectAttributes) {
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        if (shops.getTotalElements() == 0) {
            return handleShopDoesNotExistSituation(redirectAttributes, memberId);
        }
        Shop shop = shops.getContent().get(0);
        newProduct.setShop(shop);
        Date now = new Date();
        newProduct.setOrder(now.getTime());
        for (int i = 0; i < cids.length; i++) {
            Link link = new Link();
            link.setProduct(newProduct);
            link.setCategory(new Category(cids[i]));
            link.setCreateDate(now);
            newProduct.getLink().add(link);
        }

        productService.saveProduct(newProduct);
        redirectAttributes.addFlashAttribute("message", "创建商品成功");
        return "redirect:/member/shop/product/";
    }

    @RequestMapping(value = "product/update/{id}", method = RequestMethod.GET)
    public String updateProductForm(@PathVariable("id") Long id, Model model) {
        model.addAttribute("product", productService.getProduct(id));
        model.addAttribute("action", "update");
        return "member/product-form";
    }

    @RequestMapping(value = "product/update", method = RequestMethod.POST)
    public String updateProduct(@Valid @ModelAttribute("preloadProduct") Product product,
            @RequestParam(value = "cid") Long[] cids, RedirectAttributes redirectAttributes) {
        if (cids != null) {
            Set<Link> links = product.getLink();
            Set<Link> removedLinks = new HashSet<Link>();
            for (Link link : links) {
                Long linkId = link.getCategory().getId();
                boolean found = false;
                for (int i = 0; i < cids.length; i++) {
                    if (linkId.equals(cids[i])) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    removedLinks.add(link);
                }
            }
            for (Link link : removedLinks) {
                links.remove(link);
                // link.setProduct(null);
                // link.setPk(null);
            }
            Set<Link> addedLinks = new HashSet<Link>();
            Date now = new Date();
            for (int i = 0; i < cids.length; i++) {
                boolean found = false;
                for (Link link : links) {
                    if (link.getCategory().getId().equals(cids[i])) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    Link link = new Link();
                    link.setCategory(new Category(cids[i]));
                    link.setProduct(product);
                    link.setCreateDate(now);
                    addedLinks.add(link);
                }
            }
            links.addAll(addedLinks);
        }
        productService.saveProduct(product);
        redirectAttributes.addFlashAttribute("message", "更新商品成功");
        return "redirect:/member/shop/product/";
    }

    @RequestMapping(value = "product/delete/{id}")
    public String deleteProduct(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
        productService.deleteProduct(id);
        redirectAttributes.addFlashAttribute("message", "商品下架成功");
        return "redirect:/member/shop/product/";
    }

    @RequestMapping(value = "product/undelete/{id}")
    public String unDeleteProduct(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
        productService.unDeleteProduct(id);
        redirectAttributes.addFlashAttribute("message", "商品上架成功");
        return "redirect:/member/shop/product/";
    }

    private static final ObjectMapper mapper = new ObjectMapper();

    @JsonAutoDetect(fieldVisibility = Visibility.PUBLIC_ONLY)
    private static class CategoryListAjaxData {
        public long id;
        public long level;
        public String name;
        public long order;
        public List<CategoryListAjaxData> sub;

        public CategoryListAjaxData(long id, long level, String name, long order) {
            this.id = id;
            this.level = level;
            this.name = name;
            this.order = order;
            this.sub = new ArrayList<CategoryListAjaxData>();
        }
    }

    @RequestMapping(value = "ajax-category-get-all", method = RequestMethod.POST)
    @ResponseBody
    public Object ajaxGetAllCategory(HttpServletRequest request) {
        Long memberId = getCurrentUserId();
        Page<Shop> shops = shopService.getMemberShop(memberId);
        Shop shop;
        if (shops.getTotalElements() == 0 || (shop = shops.getContent().get(0)) == null) {
            return "{ \"err\": " + ERR_CODE_OBJECT_NOT_FOUND + "}";
        }
        Page<Category> category = categoryService.getShopCategory(shop.getId());
        List<Category> c0 = category.getContent();
        List<Category> c = new ArrayList<Category>(c0.size());
        for (int i = 0; i < c0.size(); i++) {
            c.add(c0.get(i));
        }
        Collections.sort(c, new CategorySorter(c));
        List<CategoryListAjaxData> data = new ArrayList<CategoryListAjaxData>();
        CategoryListAjaxData l1 = null, l2 = null;
        for (int i = 0; i < c.size(); i++) {
            Category one = c.get(i);
            CategoryListAjaxData one2 = new CategoryListAjaxData(one.getId(), one.getLevel(), one.getName(), one.getOrder());
            if (one.getLevel() == 1) {
                l1 = one2;
                l2 = null;
                data.add(l1);
            } else if (one.getLevel() == 2) {
                l2 = one2;
                l1.sub.add(l2);
            } else if (one.getLevel() == 3) {
                l2.sub.add(one2);
            }
        }
        try {
            return "{\"cids\":" + mapper.writeValueAsString(data) + "}";
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return "{\"cids\": []}";
        }
    }

    @RequestMapping(value = "ajax-category-add", method = RequestMethod.POST)
    @ResponseBody
    public Object ajaxAddCategory(@RequestParam int level,
            @RequestParam(value = "l1id", defaultValue = "") String l1cid,
            @RequestParam(value = "l2id", defaultValue = "") String l2cid,
            @RequestParam(value = "name", defaultValue = "") String name,
            @RequestParam(value = "order", defaultValue = "-1") int order, HttpServletRequest request) {
        int err = 0;
        String returnStr = "{}";
        for (int i = 0; i < 1; i++) {
            Long memberId = getCurrentUserId();
            Page<Shop> shops = shopService.getMemberShop(memberId);
            if (shops.getTotalElements() == 0) {
                err = ERR_CODE_OBJECT_NOT_FOUND;
                break;
            }
            Shop shop = shops.getContent().get(0);
            if (shop == null) {
                err = ERR_CODE_OBJECT_NOT_FOUND;
                break;
            }
            // Check required input.
            if (level == 2 && l1cid.isEmpty() || level == 3 && (l1cid.isEmpty() || l1cid.isEmpty()) || name.isEmpty()
                    || order < 0) {
                err = ERR_CODE_MISSING_REQUIRED_DATA;
                break;
            }
            // Proceed
            Category category = new Category();
            category.setLevel(level);
            switch (level) {
            case 3:
                category.setLevel2Id(Long.valueOf(l2cid));
            case 2:
                category.setLevel1Id(Long.valueOf(l1cid));
            }
            category.setName(name);
            category.setOrder(order);
            category.setShop(shop);
            categoryService.saveCategory(category);
            switch (category.getLevel()) {
            case 1:
                category.setLevel1Id(category.getId());
                break;
            case 2:
                category.setLevel2Id(category.getId());
                break;
            case 3:
                category.setLevel3Id(category.getId());
                break;
            }
            categoryService.saveCategory(category);
            // return id of category added to client
            returnStr = "{\"id\":" + category.getId() + "}";
        }
        // 返回json数据
        return err == 0 ? returnStr : "{ \"err\": " + err + "}";
    }

    @RequestMapping(value = "ajax-category-modify", method = RequestMethod.POST)
    @ResponseBody
    public Object ajaxModifyCategory(@RequestParam long cid, @RequestParam String name, @RequestParam int order,
            HttpServletRequest request) {
        Category category = categoryService.getCategory(cid);
        if (category == null) {
            // 返回json数据
            return "{ \"err\": " + ERR_CODE_OBJECT_NOT_FOUND + "}";
        } else {
            category.setName(name);
            category.setOrder(order);
            categoryService.saveCategory(category);
            // 返回json数据
            return "{}";
        }
    }

    @RequestMapping(value = "ajax-category-delete", method = RequestMethod.POST)
    @ResponseBody
    public Object ajaxDeleteCategory(@RequestParam long cid, HttpServletRequest request) {
        // TODO: DBException handler
        categoryService.deleteCategory(cid);
        // 返回json数据
        return "{}";
    }

    /**
     * 取出Shiro中的当前用户Id.
     */
    private Long getCurrentUserId() {
        ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
        return user.id;
    }

    @ModelAttribute("preloadShop")
    public Shop getShop(@RequestParam(value = "id", required = false) Long id) {
        if (id != null) {
            return shopService.getShop(id);
        }
        return new Shop();
    }

    // Forward shop/category/product management pages to member/shop-home when
    // the shop doesn't exist yet.
    private String handleShopDoesNotExistSituation(Model model, Long memberId) {
        Shop shop = new Shop();
        MemberUser currentUser = new MemberUser(memberId);
        shop.setMemberUser(currentUser);
        model.addAttribute("shop", shop);
        return "member/shop-home";
    }

    // Forward shop/category/product management pages to member/shop-home when
    // the shop doesn't exist yet.
    private String handleShopDoesNotExistSituation(RedirectAttributes redirectAttributes, Long memberId) {
        Shop shop = new Shop();
        MemberUser currentUser = new MemberUser(memberId);
        shop.setMemberUser(currentUser);
        redirectAttributes.addAttribute("shop", shop);
        return "redirect:/member/shop-home";
    }

    private class CategorySorter implements Comparator<Category> {
        private Map<Long, Integer> map = new HashMap<Long, Integer>();
        CategorySorter(List<Category> category) {
            for (int i = 0; i < category.size(); i++) {
                map.put(category.get(i).getId(), category.get(i).getOrder());
            }
        }
        @Override
        public int compare(Category o1, Category o2) {
            int order1, order2;
            if ((order1 = map.get(o1.getLevel1Id())) != (order2 = map.get(o2.getLevel1Id()))) {
                return order1 - order2;
            }
            if (o1.getLevel2Id() == null || o2.getLevel2Id() == null) {
                return o1.getLevel2Id() == null ? -1 : 1;
            }
            if ((order1 = map.get(o1.getLevel2Id())) != (order2 = map.get(o2.getLevel2Id()))) {
                return order1 - order2;
            }
            if (o1.getLevel3Id() == null || o2.getLevel3Id() == null) {
                return o1.getLevel3Id() == null ? -1 : 1;
            }
            return map.get(o1.getLevel3Id()) - map.get(o2.getLevel3Id());
        }
    }

    @ModelAttribute("preloadProduct")
    public Product getProduct(@RequestParam(value = "id", required = false) Long id) {
        if (id != null) {
            return productService.getProduct(id);
        }
        return new Product();
    }
}
